﻿using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System;
using System.Threading;

public class NetSocket
{
    public enum ErrorSocket
    {
        Scuess = 0,
        timeOut,
        socketNull,
        socketUnConect,
        ConnectUnSuceessUNknow,
        connectSucess,
        connectError,
        SendUnSucessUnKwon,
        RecvUnSucessUnKnow,
        DisConnectUnKnow,
    }

    private ErrorSocket errorSocket;

    public delegate void CallBackNormal(bool sucess, ErrorSocket error, string exception);

    public delegate void CallBackRecv(bool sucess, ErrorSocket error, string exception, byte[] bytes, string msg);

    public CallBackNormal callBackConnect;
    public CallBackNormal callBackSend;
    public CallBackRecv callBackRecv;
    public CallBackNormal callDisConnect;

    private Socket client;
    private string addressIp;
    private ushort port;

    SocketBuffer recvBuffer;
    byte[] recvBuf;
    public NetSocket()
    {
        recvBuffer = new SocketBuffer(6, RecvMsgOver);
        recvBuf = new byte[1024];
    }

    #region connect

    public bool IsConnected { get { return client != null && client.Connected; } }

    public void AsynConnect(string ip, ushort port, CallBackNormal connectBack, CallBackRecv tmpCallbackRecv)
    {
        errorSocket = ErrorSocket.Scuess;
        this.callBackConnect = connectBack;

        this.callBackRecv = tmpCallbackRecv;

        if (client != null && client.Connected)
        {
            this.callBackConnect(false, ErrorSocket.connectError, "Connect repest");
        }
        else if (client == null || !client.Connected)
        {
            client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            IPAddress ipAdress = IPAddress.Parse(ip);
            IPEndPoint endPoint = new IPEndPoint(ipAdress, port);

            IAsyncResult connect = client.BeginConnect(endPoint, ConnectCallBack, client);
            if (!writeDot(connect))
            {
                connectBack(false, ErrorSocket.connectError, "connect time out");
            }
        }
    }

    private void ConnectCallBack(IAsyncResult asconnect)
    {
        try
        {
            client.EndConnect(asconnect);

            if (!client.Connected)
            {
                errorSocket = ErrorSocket.connectError;
                callBackConnect(false, ErrorSocket.connectError, "connect time out");
                return;
            }
            else
            {
                callBackConnect(false, ErrorSocket.connectSucess, "sucess");
            }
        }
        catch (Exception ex)
        {
            errorSocket = ErrorSocket.connectError;
            callBackConnect(false, ErrorSocket.connectError, ex.Message);
        }
    }
    #endregion
    #region time out
    //true 可以写入读取  false超时
    bool writeDot(IAsyncResult ar)
    {
        int i = 0;
        while (ar.IsCompleted == false)
        {
            i++;
            if (i > 20)
            {
                errorSocket = ErrorSocket.timeOut;
                return false;
            }
            Thread.Sleep(100);
        }
        return true;
    }

    #endregion
    #region recv

    public void Recive()
    {
        if (client != null && client.Connected)
        {
            IAsyncResult res = client.BeginReceive(recvBuf, 0, recvBuf.Length, SocketFlags.None, ReciveCallBack, client);

            if (!writeDot(res))
            {
                callBackRecv(false, ErrorSocket.RecvUnSucessUnKnow, "recv false", null, "");
            }
        }
    }

    private void ReciveCallBack(IAsyncResult res)
    {
        try
        {
            client.EndReceive(res);
            if (!client.Connected)
            {
                callBackRecv(false, ErrorSocket.RecvUnSucessUnKnow, "connect false", null, "");
            }

            int length = client.EndReceive(res);

            if (length == 0) return;

            recvBuffer.RecvByte(recvBuf, length);
        }
        catch
        {
            callBackRecv(false, ErrorSocket.RecvUnSucessUnKnow, "", null, "");
        }

        Recive();
    }
    public void RecvMsgOver(byte[] alldata)
    {
        callBackRecv(true, ErrorSocket.connectSucess, "recv back sucess", alldata, "");
    }
    #endregion
    #region Send
    public void AsynSend(byte[] sendBuffer, CallBackNormal tmpSendBack)
    {
        errorSocket = ErrorSocket.Scuess;

        this.callBackSend = tmpSendBack;

        if (client == null)
        {
            this.callBackSend(false, ErrorSocket.socketNull, "");
        }
        else if (!client.Connected)
        {
            this.callBackSend(false, ErrorSocket.socketUnConect, "");
        }
        else
        {
            IAsyncResult asyncResult = client.BeginSend(sendBuffer, 0, sendBuffer.Length, SocketFlags.None, SendCallback, client);
            if (!writeDot(asyncResult))
            {
                this.callBackSend(false, ErrorSocket.SendUnSucessUnKwon, "");
            }
        }
    }

    private void SendCallback(IAsyncResult res)
    {
        try
        {
            int byteSend = client.EndSend(res);

            if (byteSend > 0)
            {
                callBackSend(true, ErrorSocket.Scuess, "send Sucess");
            }
            else
            {
                callBackSend(false, ErrorSocket.SendUnSucessUnKwon, "send lose");
            }
        }
        catch
        {

        }

    }
    #endregion
    #region disconnect
    public void AsyncDisconnect(CallBackNormal tmpDisConnect)
    {
        try
        {
            errorSocket = ErrorSocket.Scuess;
            this.callDisConnect = tmpDisConnect;

            if (client == null)
            {
                this.callDisConnect(false, ErrorSocket.DisConnectUnKnow, "client is null");
            }
            else if (!client.Connected)
            {
                this.callDisConnect(false, ErrorSocket.DisConnectUnKnow, "client is disConnected");
            }
            else
            {
                IAsyncResult res = client.BeginDisconnect(false, DisconnectBack, client);
                if (!writeDot(res))
                {
                    this.callDisConnect(false, ErrorSocket.DisConnectUnKnow, "client is DisConnectUnKnow");
                }
            }
        }
        catch (Exception ex)
        {
            this.callDisConnect(false, ErrorSocket.DisConnectUnKnow, ex.Message);
        }
    }

    private void DisconnectBack(IAsyncResult res)
    {
        try
        {
            client.EndDisconnect(res);
            client.Close();
            client = null;
            this.callDisConnect(true, ErrorSocket.Scuess, "client is sucess disconnect");
        }
        catch (Exception ex)
        {
            this.callDisConnect(false, ErrorSocket.DisConnectUnKnow, ex.Message);
        }

    }
    #endregion
}
